home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 1 / Atari Mega Archive - Volume 1.iso / gnu / progutil / stdwin.zoo / test / tetris.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-03-30  |  16.7 KB  |  805 lines

  1. /* Tetris.
  2.  
  3.    A simple but challenging game where pieces of different shapes
  4.    falling with a constant speed must be manoeuvered to a final
  5.    docking position.  Piece shapes are randomly chosen from all possible
  6.    ways to connect four squares.  The manipulations allowed are moving
  7.    the piece left or right and rotating it; every clock tick it moves
  8.    down one step, until it cannot move further, or the player decides
  9.    to "drop" it to earn more points.  The game is made more or less
  10.    challenging by making the clock tick faster or slower.  A score is
  11.    kept.  Points earned per piece depend on the height where it is
  12.    dropped or stopped; extra points are awarded for choosing a higher
  13.    speed ("level").  To allow the play to continue indefinitely,
  14.    complete rows (i.e., horizontal rows where all squares
  15.    are filled) are removed from the board and its contents above that
  16.    row shifted down.
  17.    
  18.    The user interface uses mostly the left, right and up keys, for
  19.    moving the piece left and right and rotating it.  For Unix adepts,
  20.    'h'=left, 'k'=up and 'l'=right also work.  Space bar or Return
  21.    drops the piece.  The Cancel key (Command-Period on the Mac,
  22.    Control-C on most other systems) restarts the game; close the window
  23.    or type 'q' to quit the game.
  24.    
  25.    The origin of the game appears to be in the East Block; I've heard
  26.    that the (original?) Macintosh version was by a Hungarian programmer.
  27.    
  28.    This code is hereby put in the public domain, but the package
  29.    STDWIN used as portable window interface is copyrighted.  STDWIN
  30.    is available from me, too, provided you respect the copyright etc.
  31.    
  32.    Guido van Rossum, CWI, Kruislaan 413, 1098 SJ Amsterdam,
  33.    The Netherlands
  34.    Internet e-mail address: guido@cwi.nl
  35.    April 1989
  36. */
  37.  
  38. /* TO DO:
  39.     - show next piece coming up in a side window
  40.     - change chance distribution of pieces? (too few bars)
  41.     - advanced level (what should it do? just faster?)
  42.     - rethink level <--> delay relation
  43.     
  44.     - improve "game over" behavior
  45.     
  46.     - display high score
  47.     - score and status display next to the board
  48.     - statistics
  49.     
  50.     - cute graphics
  51. */
  52.  
  53. /* Standard include files */
  54.  
  55. #include "stdwin.h"
  56. #include <stdio.h>
  57.  
  58. #ifdef __STDC__
  59. #include <stddef.h>
  60. #include <stdlib.h>
  61. # define    P(s) s
  62. #else
  63. # define P(s) ()
  64. #endif
  65.  
  66.  
  67. /* tetris.c */
  68. void settitle P((void));
  69. void eraseboard P((int ileft , int itop , int iright , int ibottom ));
  70. void drawboard P((int ileft , int itop , int iright , int ibottom ));
  71. void redrawboard P((int ileft , int itop , int iright , int ibottom ));
  72. void drawproc P((WINDOW *win , int left , int top , int right , int bottom ));
  73. int allowed P((int dh , int dv ));
  74. int uniform P((int n ));
  75. void leftrotate P((void ));
  76. int moveby P((int dh , int dv ));
  77. int rotateby P((int n ));
  78. void left P((void ));
  79. void right P((void ));
  80. void rot P((void ));
  81. void generate P((void ));
  82. void reset P((void ));
  83. void removerows P((void ));
  84. void addscore P((void ));
  85. void finish P((void ));
  86. void timer P((void ));
  87. void faster P((void ));
  88. void slower P((void ));
  89. void quit P((void ));
  90. void mainloop P((void ));
  91. void addhelpmenu P((void ));
  92.  
  93. #undef P
  94.  
  95. /* Parametrizations */
  96.  
  97. /* Piece size.  It only makes sense to change this if you also
  98.    change the initialization of the 'shapes' array.
  99.    Since we rotate pieces, their max size must always be square. */ 
  100. #define PSIZE 4
  101.  
  102. /* Game dimensions.  Traditionally it is played on a 10x20 board. */
  103. #ifndef BWIDTH
  104. #define BWIDTH 10
  105. #endif
  106. #ifndef BHEIGHT
  107. #define BHEIGHT 20
  108. #endif
  109.  
  110. /* Initial timer delay.  This affects initial difficulty and scoring.
  111.    The current value is kept in variable 'delay'. */
  112. #ifndef DELAY
  113. #define DELAY 10
  114. #endif
  115.  
  116. /* Individual 'square' sizes.
  117.    These can be adjusted according to taste and the size of pixels
  118.    on your screen.  (You can also fine-tune window and document size
  119.    in main() below.)
  120.    For the alfa version of STDWIN, where pixel size == character size,
  121.    a fixed size of 2x1 is forced later. */
  122. #ifndef SQWIDTH
  123. #define SQWIDTH 12
  124. #endif
  125. #ifndef SQHEIGHT
  126. #define SQHEIGHT 12
  127. #endif
  128.  
  129. /* Left, top of board image in window */
  130. #ifndef BLEFT
  131. #define BLEFT 0
  132. #endif
  133. #ifndef BTOP
  134. #define BTOP 4
  135. #endif
  136.  
  137. /* Some useful macros (predefined on some but not all systems) */
  138.  
  139. #ifdef MIN
  140. #undef MIN
  141. #endif
  142. #define MIN(a, b) ((a) < (b) ? (a) : (b))
  143.  
  144. #ifdef MAX
  145. #undef MAX
  146. #endif
  147. #define MAX(a, b) ((a) > (b) ? (a) : (b))
  148.  
  149. /* Available shapes.
  150.    Relnext is the offset to the next piece after rotation.
  151.    The pieces are aligned with the bottom so their scoring values are
  152.    comparable, and the delay before their start is minimal; they are
  153.    centered horizontally so the random placement appears even.
  154.    Remember C's aggregate initialization rules; the initializer below
  155.    is completely bracketed, but trailing zeros are sometimes elided. */
  156.  
  157. struct shapedef {
  158.     int relnext;
  159.     char piece[PSIZE][PSIZE];
  160. } shapes[] = {
  161.     
  162.     /* "Four in a row" (2 orientations) */
  163.     
  164.     {1,    {{0, 0, 0, 0},
  165.          {0, 0, 0, 0},
  166.          {0, 0, 0, 0},
  167.          {1, 1, 1, 1}}},
  168.     
  169.     {-1,    {{0, 1, 0, 0},
  170.          {0, 1, 0, 0},
  171.          {0, 1, 0, 0},
  172.          {0, 1, 0, 0}}},
  173.     
  174.     /* "L shape" (4 orientations) */
  175.     
  176.     {1,    {{0, 0, 0},
  177.          {0, 1, 0},
  178.          {0, 1, 0},
  179.          {0, 1, 1}}},
  180.     
  181.     {1,    {{0, 0, 0},
  182.          {0, 0, 0},
  183.          {0, 0, 1},
  184.          {1, 1, 1}}},
  185.     
  186.     {1,    {{0, 0, 0},
  187.          {0, 1, 1},
  188.          {0, 0, 1},
  189.          {0, 0, 1}}},
  190.     
  191.     {-3,    {{0, 0, 0},
  192.          {0, 0, 0},
  193.          {1, 1, 1},
  194.          {1, 0, 0}}},
  195.     
  196.     /* "Inverse L shape" (4 orientations) */
  197.     
  198.     {1,    {{0, 0, 0},
  199.          {0, 1, 1},
  200.          {0, 1, 0},
  201.          {0, 1, 0}}},
  202.     
  203.     {1,    {{0, 0, 0},
  204.          {0, 0, 0},
  205.          {1, 0, 0},
  206.          {1, 1, 1}}},
  207.     
  208.     {1,    {{0, 0, 0},
  209.          {0, 0, 1},
  210.          {0, 0, 1},
  211.          {0, 1, 1}}},
  212.     
  213.     {-3,    {{0, 0, 0},
  214.          {0, 0, 0},
  215.          {1, 1, 1},
  216.          {0, 0, 1}}},
  217.     
  218.     /* "Z shape" (2 orientations) */
  219.     
  220.     {1,    {{0, 0, 0},
  221.          {0, 0, 0},
  222.          {1, 1, 0},
  223.          {0, 1, 1}}},
  224.     
  225.     {-1,    {{0, 0, 0},
  226.          {0, 0, 1},
  227.          {0, 1, 1},
  228.          {0, 1, 0}}},
  229.     
  230.     /* "S shape" (2 orientations) */
  231.     
  232.     {1,    {{0, 0, 0},
  233.          {0, 1, 0},
  234.          {0, 1, 1},
  235.          {0, 0, 1}}},
  236.     
  237.     {-1,    {{0, 0, 0},
  238.          {0, 0, 0},
  239.          {0, 1, 1},
  240.          {1, 1, 0}}},
  241.     
  242.     /* "T shape" (4 orientations) */
  243.     
  244.     {1,    {{0, 0, 0},
  245.          {0, 0, 0},
  246.          {1, 1, 1},
  247.          {0, 1, 0}}},
  248.     
  249.     {1,    {{0, 0, 0},
  250.          {0, 1, 0},
  251.          {0, 1, 1},
  252.          {0, 1, 0}}},
  253.     
  254.     {1,    {{0, 0, 0},
  255.          {0, 0, 0},
  256.          {0, 1, 0},
  257.          {1, 1, 1}}},
  258.     
  259.     {-3,    {{0, 0, 0},
  260.          {0, 0, 1},
  261.          {0, 1, 1},
  262.          {0, 0, 1}}},
  263.     
  264.     /* "Block" (1 orientation) */
  265.     
  266.     {0,    {{0, 0, 0},
  267.          {0, 0, 0},
  268.          {0, 1, 1},
  269.          {0, 1, 1}}},
  270.  
  271. };
  272.  
  273. /* Global variables */
  274.  
  275. int alfa;            /* Nonzero if using alfa STDWIN */
  276. int sqwidth = SQWIDTH;        /* Width of squares, in pixels */
  277. int sqheight = SQHEIGHT;    /* Height */
  278. int bleft = BLEFT;
  279. int btop = BTOP;
  280. WINDOW *win;            /* The window where we do our drawing */
  281. int delay;            /* Current delay */
  282.                 /* NB: level = MAX(0, DELAY-delay) */
  283. char board[BHEIGHT][BWIDTH];    /* Contents of board, except current piece */
  284. char (*piece)[PSIZE];        /* Piece currently being manoeuvered */
  285. int pindex;            /* Index in the shape array of current piece */
  286. int pleft, ptop;        /* Position of current piece */
  287. long score;            /* Score of current game */
  288.  
  289. /* Generate an informative title from level and/or score.
  290.    (By putting it in the title bar we don't need an info window.) */
  291.  
  292. void
  293. settitle()
  294. {
  295.     char buf[100];
  296.     int level = MAX(0, DELAY-delay);
  297.     
  298.     if (level == 0) {
  299.         if (score == 0)
  300.             strcpy(buf, "Tetris");
  301.         else
  302.             sprintf(buf, "Score %ld", score);
  303.     }
  304.     else {
  305.         if (score == 0)
  306.             sprintf(buf, "Level %d", level);
  307.         else
  308.             sprintf(buf, "Sc %ld Lv %d", score, level);
  309.     }
  310.     wsettitle(win, buf);
  311. }
  312.  
  313. /* Erase a portion of the board on the screen.
  314.    Call only within wbegin/enddrawing. */
  315.  
  316. void
  317. eraseboard(ileft, itop, iright, ibottom)
  318.     int ileft, itop, iright, ibottom;
  319. {
  320.     werase(bleft + ileft*sqwidth,  btop + itop*sqheight,
  321.            bleft + iright*sqwidth, btop + ibottom*sqheight);
  322. }
  323.  
  324. /* Draw a portion of the board, and a border around it.
  325.    Call only within wbegin/enddrawing.
  326.    This may be called with out-of-range parameters.
  327.    Draw those squares of the game that lie (partly) in the rectangle
  328.    given by the parameters.  Assume the background is blank.
  329.    This contains #ifdef'ed code for the alfa version of STDWIN,
  330.    which only supports text output. */
  331.  
  332. void
  333. drawboard(ileft, itop, iright, ibottom)
  334.     int ileft, itop, iright, ibottom;
  335. {
  336.     int ih, iv;
  337.     int h, v;
  338.     int flag;
  339.     
  340.     ileft = MAX(0, ileft);
  341.     itop = MAX(0, itop);
  342.     iright = MIN(BWIDTH, iright);
  343.     ibottom = MIN(BHEIGHT, ibottom);
  344.     for (iv = itop, v = btop + iv*sqheight; iv < ibottom;
  345.                     ++iv, v += sqheight) {
  346.         for (ih = ileft, h = bleft + ih*sqwidth; ih < iright;
  347.                         ++ih, h += sqwidth) {
  348.             flag = board[iv][ih];
  349.             if (!flag && pleft <= ih && ih < pleft+PSIZE &&
  350.                     ptop <= iv && iv < ptop+PSIZE)
  351.                 flag = piece[iv-ptop][ih-pleft];
  352.             if (flag) {
  353.                 if (alfa)
  354.                     wdrawchar(h, v, '#');
  355.                 else
  356.                     wshade(h+1, v+1,
  357.                         h+sqwidth, v+sqheight, 50);
  358.             }
  359.         }
  360.     }
  361.     if (alfa) {
  362.         /* Draw markers at the right margin */
  363.         wdrawchar(bleft + BWIDTH*sqwidth, btop, '|');
  364.         wdrawchar(bleft + BWIDTH*sqwidth, btop + (BHEIGHT-1)*sqheight,
  365.                                     '|');
  366.     }
  367.     else {
  368.         /* Draw a box around the board */
  369.         wdrawbox(bleft - 1, btop - 1, bleft + BWIDTH*sqwidth + 2,
  370.                         btop + BHEIGHT*sqheight + 2);
  371.     }
  372. }
  373.  
  374. /* Erase and redraw part of the board.
  375.    Unlike eraseboard and drawboard above, this includes calls to
  376.    wbegin/enddrawing. */
  377.  
  378. void
  379. redrawboard(ileft, itop, iright, ibottom)
  380.     int ileft, itop, iright, ibottom;
  381. {
  382.     wbegindrawing(win);
  383.     eraseboard(ileft, itop, iright, ibottom);
  384.     drawboard(ileft, itop, iright, ibottom);
  385.     wenddrawing(win);
  386. }
  387.  
  388. /* Draw procedure, passed to STDWIN's wopen */
  389.  
  390. void
  391. drawproc(win, left, top, right, bottom)
  392.     WINDOW *win;
  393.     int left, top, right, bottom;
  394. {
  395.     drawboard((left-bleft)/sqwidth, (top-btop)/sqheight,
  396.         (right-bleft+sqwidth-1)/sqwidth,
  397.         (bottom-btop+sqheight-1)/sqheight);
  398. }
  399.  
  400. /* Check if the piece can be at (dh, dv).
  401.    This is used to check for legal moves.
  402.    No part of the piece can be on a filled spot in the board or
  403.    be outside it, but it can stick out above the top. */
  404.  
  405. int
  406. allowed(dh, dv)
  407.     int dh, dv;
  408. {
  409.     int ih, iv;
  410.     
  411.     for (iv = 0; iv < PSIZE; ++iv) {
  412.         for (ih = 0; ih < PSIZE; ++ih) {
  413.             if (piece[iv][ih]) {
  414.                 if (ih+dh < 0 || ih+dh >= BWIDTH)
  415.                     return 0;
  416.                 if (iv+dv < 0)
  417.                     continue;
  418.                 if (iv+dv >= BHEIGHT)
  419.                     return 0;
  420.                 if (board[iv+dv][ih+dh])
  421.                     return 0;
  422.             }
  423.         }
  424.     }
  425.     return 1;
  426. }
  427.  
  428. /* Return a random integer in the range [0..n-1] */
  429.  
  430. int
  431. uniform(n)
  432.     int n;
  433. {
  434.     return rand() % n;
  435. }
  436.  
  437. /* Rotate the piece 90 degrees counterclockwise.  No drawing is done.
  438.    The implementation is trivial: just take the "next" element from the
  439.    shape array. */
  440.  
  441. void
  442. leftrotate()
  443. {
  444.     pindex += shapes[pindex].relnext;
  445.     piece = shapes[pindex].piece;
  446. }
  447.  
  448. /* Move the piece by the given vector (dh, dv), if this is a legal move..
  449.    Return 1 if moved, 0 if not (then no changes were made). */
  450.  
  451. int
  452. moveby(dh, dv)
  453.     int dh, dv;
  454. {
  455.     int ileft, itop, iright, ibottom;
  456.     
  457.     if (!allowed(pleft+dh, ptop+dv)) {
  458.         return 0;
  459.     }
  460.     ileft   = pleft + MIN(dh, 0);
  461.     itop    = ptop  + MIN(dv, 0);
  462.     iright  = pleft + PSIZE + MAX(dh, 0);
  463.     ibottom = ptop  + PSIZE + MAX(dv, 0);
  464.     pleft += dh;
  465.     ptop += dv;
  466.     redrawboard(ileft, itop, iright, ibottom);
  467.     return 1;
  468. }
  469.  
  470. /* Rotate the piece n quarter left turns, if this is a legal move.
  471.    Return 1 if moved, 0 if not (then no changes were made). */
  472.  
  473. int
  474. rotateby(n)
  475.     int n;
  476. {
  477.     int i;
  478.     
  479.     for (i = 0; i < n; ++i)
  480.         leftrotate();
  481.     if (!allowed(pleft, ptop)) {
  482.         for (i = 0; i < 4-n; ++i)
  483.             leftrotate();
  484.         return 0;
  485.     }
  486.     redrawboard(pleft, ptop, (pleft+PSIZE), (ptop+PSIZE));
  487.     return 1;
  488. }
  489.  
  490.  
  491. /* Trivial routines to implement the commands. */
  492.  
  493. void
  494. left()
  495. {
  496.     (void) moveby(-1, 0);
  497. }
  498.  
  499. void
  500. right()
  501. {
  502.     (void) moveby(1, 0);
  503. }
  504.  
  505. void
  506. rot()
  507. {
  508.     (void) rotateby(1);
  509. }
  510.  
  511. /* Generate a new piece.  Its initial position is just above the top of
  512.    the board, so that a single move down will show its bottom row.
  513.    (This is one reason why the pieces are aligned with the bottom in the
  514.    'shapes' array.) */
  515.  
  516. void
  517. generate()
  518. {
  519.     pindex = uniform((int)sizeof shapes / (int)sizeof shapes[0]);
  520.     
  521.     piece = shapes[pindex].piece;
  522.     pleft = (BWIDTH-PSIZE) / 2;
  523.     ptop = -PSIZE;
  524. }
  525.  
  526. /* Start a new game.
  527.    Reset deley/level, score, board and title; generate a new piece.
  528.    The game is not restarted immediately. */
  529.  
  530. void
  531. reset()
  532. {
  533.     int ih, iv;
  534.     
  535.     wsettimer(win, 0);
  536.     delay = DELAY;
  537.     score = 0;
  538.     for (iv = 0; iv < BHEIGHT; ++iv) {
  539.         for (ih = 0; ih < BWIDTH; ++ih)
  540.             board[iv][ih] = 0;
  541.     }
  542.     generate();
  543.     redrawboard(0, 0, BWIDTH, BHEIGHT);
  544.     settitle();
  545. }
  546.  
  547. /* Remove any full rows found, shifting the board above down */
  548.  
  549. void
  550. removerows()
  551. {
  552.     int ih, iv;
  553.     int jv;
  554.     
  555.     for (iv = 0; iv < BHEIGHT; ++iv) {
  556.         for (ih = 0; ih < BWIDTH; ++ih) {
  557.             if (!board[iv][ih])
  558.                 goto next; /* Two-level continue */
  559.         }
  560.         for (jv = iv; jv > 0; --jv) {
  561.             for (ih = 0; ih < BWIDTH; ++ih)
  562.                 board[jv][ih] = board[jv-1][ih];
  563.         }
  564.         for (ih = 0; ih < BWIDTH; ++ih)
  565.             board[jv][ih] = 0;
  566.         wscroll(win,
  567.             bleft, btop,
  568.             bleft + BWIDTH*sqwidth, btop + (iv+1)*sqheight,
  569.             0, sqheight);
  570.     next:    ;
  571.     }
  572. }
  573.  
  574. /* Add the score for the current piece to the total score.
  575.    The title is not regenerated; that is done later in finish(). */
  576.  
  577. void
  578. addscore()
  579. {
  580.     int level = MAX(0, DELAY-delay);
  581.     int height = MAX(0, BHEIGHT-ptop);
  582.     
  583.     score += height + 2*level /* *(advanced?2:1) */ ;
  584. }
  585.  
  586. /* Finish a piece off by dropping it; score and generate a new one.
  587.    Called by the user and from timer of the piece can't move down.
  588.    This also contains a hack to detect the end of the game:
  589.    if the new piece can't move one step, it is over. */
  590.  
  591. void
  592. finish()
  593. {
  594.     int ih, iv;
  595.     
  596.     addscore();
  597.     while (moveby(0, 1))
  598.         ;
  599.     for (iv = 0; iv < PSIZE; ++iv) {
  600.         for (ih = 0; ih < PSIZE; ++ih) {
  601.             if (piece[iv][ih] && iv+ptop >= 0)
  602.                 board[iv+ptop][ih+pleft] = 1;
  603.         }
  604.     }
  605.     removerows();
  606.     generate();
  607.     settitle();
  608.     if (moveby(0, 1))
  609.         wsettimer(win, delay);
  610.     else {
  611.         if (alfa) {
  612.             /* Alfa STDWIN's wmessage doesn't wait for an OK */
  613.             char buffer[10];
  614.             strcpy(buffer, "Game over");
  615.             (void) waskstr("", buffer, (int)strlen(buffer));
  616.         }
  617.         else
  618.             wmessage("Game over");
  619.         reset();
  620.     }
  621. }
  622.  
  623. /* The clock has ticked.
  624.    Try to move down; if it can't, finish it off and start a new one. */
  625.  
  626. void
  627. timer()
  628. {
  629.     if (moveby(0, 1))
  630.         wsettimer(win, delay);
  631.     else
  632.         finish();
  633. }
  634.  
  635. /* Make the clock tick faster (increase level) */
  636.  
  637. void
  638. faster()
  639. {
  640.     if (delay > 1)
  641.         --delay;
  642.     settitle();
  643. }
  644.  
  645. /* Make the clock tick slower (decrease level) */
  646.  
  647. void
  648. slower()
  649. {
  650.     ++delay;
  651.     settitle();
  652. }
  653.  
  654. /* Quit the program.
  655.    Note that wdone() MUST be called before a STDWIN application exits. */
  656.  
  657. void
  658. quit()
  659. {
  660.     wdone();
  661.     exit(0);
  662. }
  663.  
  664. /* Main event loop.
  665.    React on commands, ignoring illegal ones, and react to clock ticks.
  666.    Call various routines to execute the commands.
  667.    Never return; calls quit() to exit. */
  668.  
  669. void
  670. mainloop()
  671. {
  672.     EVENT e;
  673.     
  674.     for (;;) {
  675.         wgetevent(&e);
  676.         
  677.         switch (e.type) {
  678.         
  679.         case WE_TIMER:
  680.             timer();
  681.             break;
  682.         
  683.         case WE_CHAR:
  684.             switch (e.u.character) {
  685.             case '+':
  686.                 faster();
  687.                 break;
  688.             case '-':
  689.                 slower();
  690.                 break;
  691.             case 'g':
  692.                 timer();
  693.                 break;
  694.             case ' ':
  695.                 finish();
  696.                 break;
  697.             case 'h':
  698.                 left();
  699.                 break;
  700.             case 'k':
  701.                  rot();
  702.                  break;
  703.             case 'l':
  704.                 right();
  705.                 break;
  706.             case 'q':
  707.                 quit();
  708.                 break;
  709.             case 'r':
  710.                 reset();
  711.                 break;
  712.             case '0': case '1': case '2': case '3': case '4':
  713.             case '5': case '6': case '7': case '8': case '9':
  714.                 delay = DELAY-(e.u.character-'0');
  715.                 delay = MAX(1, delay);
  716.                 settitle();
  717.                 break;
  718.             }
  719.             break;
  720.         
  721.         case WE_COMMAND:
  722.             switch (e.u.command) {
  723.             case WC_RETURN:
  724.                 timer();
  725.                 break;
  726.             case WC_CANCEL:
  727.                 reset();
  728.                 break;
  729.             case WC_CLOSE:
  730.                 quit();
  731.                 break;
  732.             case WC_LEFT:
  733.                 left();
  734.                 break;
  735.             case WC_UP:
  736.                 rot();
  737.                 break;
  738.             case WC_RIGHT:
  739.                 right();
  740.                 break;
  741.             }
  742.             break;
  743.         
  744.         }
  745.     }
  746.     /*NOTREACHED*/
  747. }
  748.  
  749. /* Add a menu, only used as a cheap way to display some help */
  750.  
  751. void
  752. addhelpmenu()
  753. {
  754.     MENU *mp;
  755.     
  756.     mp = wmenucreate(1, "Help");
  757.     wmenuadditem(mp, "g or return starts the game", -1);
  758.     wmenuadditem(mp, "", -1);
  759.     wmenuadditem(mp, "h or left arrow moves left", -1);
  760.     wmenuadditem(mp, "l or right arrow moves right", -1);
  761.     wmenuadditem(mp, "k or up arrow rotates", -1);
  762.     wmenuadditem(mp, "space drops", -1);
  763.     wmenuadditem(mp, "", -1);
  764.     wmenuadditem(mp, "+/- increases/decreases level (speed)", -1);
  765.     wmenuadditem(mp, "0-9 chooses level directly", -1);
  766.     wmenuadditem(mp, "", -1);
  767.     wmenuadditem(mp, "r or cancel restarts", -1);
  768.     wmenuadditem(mp, "close or q quits the game", -1);
  769. }
  770.  
  771. /* Main program.
  772.    Initialize STDWIN, create the window and the help menu,
  773.    reset the game and call 'mainloop()' to play it. */
  774.  
  775. main(argc, argv)
  776.     int argc;
  777.     char **argv;
  778. {
  779.     long t;
  780.     
  781.     time(&t);
  782.     srand((short)t ^ (short)(t>>16));
  783.     winitnew(&argc, &argv);
  784.     if (wlineheight() == 1) {
  785.         alfa = 1;
  786.         sqwidth = 2;
  787.         sqheight = 1;
  788.         bleft = btop = 0;
  789.     }
  790.     wsetdefwinsize(bleft + BWIDTH*sqwidth + 1,
  791.                     btop + BHEIGHT*sqheight + 5);
  792.     win = wopen("Tetris", drawproc);
  793.     if (win == NULL) {
  794.         printf("Can't create window\n");
  795.         wdone();
  796.         exit(1);
  797.     }
  798.     wsetdocsize(win,
  799.         bleft + BWIDTH*sqwidth + 1, btop + BHEIGHT*sqheight + 1);
  800.     addhelpmenu();
  801.     reset();
  802.     mainloop();
  803.     /*NOTREACHED*/
  804. }
  805.